package com_.dxy_test_.guava_;


import com.alibaba.fastjson.JSON;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;

import java.util.HashMap;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @Description 用guava实现本地缓存
 *
 * Guava缓存，谷歌开源的一种本地缓存，使用本节点的内存来存储的，实现原理类似于ConcurrentHashMap，
 * 使用多个segments方式的细粒度锁，在保证线程安全的同时，支持高并发场景需求，
 * 同时支持多种类型的缓存清理策略，包括基于容量的清理、基于时间的清理、基于引用的清理等。
 *
 * @Author dangxianyue
 * @Date 2023/4/1 23:35
 */
public class GuavaCacheDemo {

    // 模拟DB
    private static final HashMap<Integer, String> map = new HashMap<>();

    public static LoadingCache<Integer, String> createGuavaCache(){

        return CacheBuilder.newBuilder()
                // 设置并发级别为5，并发级别是指可以同时写缓存的线程数
                .concurrencyLevel(5)
                // 设置写缓存后10秒钟后过期
                .expireAfterWrite(10, TimeUnit.SECONDS)
                // 设置缓存容器的初始容量为8
                .initialCapacity(8)
                // 设置缓存最大容量为10，超过10之后就会按照LRU最近虽少使用算法来移除缓存项
                .maximumSize(10)
                // 设置统计缓存的各种统计信息（生产坏境关闭）
                .recordStats()
                // 设置缓存的移除通知
                .removalListener(new RemovalListener<Object, Object>() {
                    @Override
                    public void onRemoval(RemovalNotification<Object, Object> notification) {
                        System.out.println(notification.getKey() + " was removed, cause is " + notification.getCause());
                    }
                })
                // 指定CacheLoader，在缓存不存在时通过CacheLoader的实现自动加载缓存
                .build(new CacheLoader<Integer, String>() {
                    @Override
                    public String load(Integer key) throws Exception {
                        // 往DB中查询数据
                        System.out.println("从DB中查询key:" + key + "的数据");
                        return Optional.ofNullable(map.get(key)).orElse("default value");// 避免返回null
                    }
                });
    }

    public static void main(String[] args) throws Exception {
//        test1();
        test2();
    }

    private static void test1() throws ExecutionException, InterruptedException {
        map.put(1,"java");
        map.put(2,"天下");
        map.put(3,"第一");

        LoadingCache<Integer, String> loadingCache = createGuavaCache();

        // 第一次缓存中没有数据，所以会往DB中查询数据
        System.out.println(loadingCache.get(2));
        // 第二次缓存中有数据，CacheLoader.load方法不会加载
        System.out.println(loadingCache.get(2));

        // 测试超过10秒后，缓存失效，会再次从db中加载
        Thread.sleep(11000);
        System.out.println(loadingCache.get(2));

        // 测试DB中不存在的数据
        System.out.println("测试DB中不存在：" + loadingCache.get(4));
    }

    private static void test2() throws ExecutionException, InterruptedException {
        // 测试超过最大容量后被淘汰
        LoadingCache<Integer, String> loadingCache = createGuavaCache();
        for (int i = 101; i <= 112; i++) {
            loadingCache.put(i, i+"");
        }

        for (int i = 101; i <= 112; i++) {
            System.out.println("key: " + i + ", value: " + loadingCache.getIfPresent(i));
        }

        // 超时淘汰
        Thread.sleep(11000);
        System.out.println("=================");
        for (int i = 101; i <= 112; i++) {
            System.out.println("key: " + i + ", value: " + loadingCache.getIfPresent(i));
        }
    }
}


